package com.secfound.soccer.server.persistence;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.SortDirection;
import com.google.appengine.api.datastore.QueryResultIterator;
import com.google.code.twig.ObjectDatastore;
import com.google.code.twig.annotation.AnnotationObjectDatastore;
import com.secfound.soccer.server.tools.DateFormatter;
import com.secfound.soccer.server.tools.GameTools;
import com.secfound.soccer.server.tools.ScoreValidator;
import com.secfound.soccer.shared.db.Bet;
import com.secfound.soccer.shared.db.Challenger;
import com.secfound.soccer.shared.db.Game;
import com.secfound.soccer.shared.db.Team;

/**
 * Functions to communicate with datastore
 * @author alfonso.ayala
 *
 */
public class DataPersistence {

	private ObjectDatastore ds = new AnnotationObjectDatastore();
	
	public String setupDB()
	{
		
		storeGame("Netherlands", "0","Spain", "1", "2010-07-11 20:30","Final","Johannesburg","");
		
		storeGame("Uruguay", "2","Netherlands", "3", "2010-07-06 20:30","Semi-Final","Cape Town","");
		storeGame("Germany", "0","Spain", "1", "2010-07-07 20:30","Semi-Final","Durban","");
		
		storeGame("Uruguay",     "4","Ghana", "2", "2010-07-02 20:30","Quarter-finals","Johannesburg","");
		storeGame("Netherlands", "2","Brazil", "1", "2010-07-02 20:30","Quarter-finals","Port Elizabeth","");
		storeGame("Argentina",   "0","Germany", "4", "2010-07-03 20:30","Quarter-finals","Cape Town","");
		storeGame("Paraguay",    "0","Spain", "1", "2010-07-03 20:30","Quarter-finals","Johannesburg","");		
		
		storeGame("Uruguay",     "2","Korea Republic", "1", "2010-06-26 20:30","Round of 16","Port Elizabeth","");		
		storeGame("USA",         "1","Ghana", "2", "2010-06-26 20:30","Round of 16","Rustenburg","");
		storeGame("Netherlands", "2","Slovakia", "1", "2010-06-28 20:30","Round of 16","Durban","");
		storeGame("Brazil",      "3","Chile", "0", "2010-06-28 20:30","Round of 16","Johannesburg","");
		storeGame("Argentina",   "3","Mexico", "1", "2010-06-27 20:30","Round of 16","Johannesburg","");
		storeGame("Germany",     "4","England", "1", "2010-06-27 20:30","Round of 16","Bloemfontein","");
		storeGame("Paraguay",    "5","Japan", "3", "2010-06-29 20:30","Round of 16","Pretoria","");
		storeGame("Spain",       "1","Portugal", "0", "2010-06-29 20:30","Round of 16","Cape Town","");
		
		storeGame("Colombia", "-","Greece",   "-", "2014-06-14 11:00","Group C","Belo Horizonte","");
		storeGame("Cote d Ivoire", "-","Japan",   "-", "2014-06-14 20:00","Group C","Recife","");
		storeGame("Colombia", "-","Cote d Ivoire",   "-", "2014-06-19 11:00","Group C","Brasilia","");
		storeGame("Japan", "-","Greece",   "-", "2014-06-19 17:00","Group C","Natal","");
		storeGame("Japan",    "-","Colombia", "-", "2014-06-24 15:00","Group C","Cuiaba",     "");
		storeGame("Greece",    "-","Cote d Ivoire", "-", "2014-06-24 15:00","Group C","Fortaleza",     "");
		
		storeTeam("Algeria","alg");
		storeTeam("Argentina","arg");
		storeTeam("Australia","aus");
		storeTeam("Belguium","bel");
		storeTeam("Bosnia-Hezgovina","bih");
		storeTeam("Brazil","bra");
		storeTeam("Chile","chi");
		storeTeam("Cote d Ivoire","civ");
		storeTeam("Cameroon","cmr");
		storeTeam("Colombia","col");
		storeTeam("Costa Rica","crc");
		storeTeam("Croatia","cro");
		storeTeam("Ecuador","ecu");
		storeTeam("England","eng");
		storeTeam("Spain","esp");
		storeTeam("France","fra");
		storeTeam("Germany","ger");
		storeTeam("Ghana","gha");
		storeTeam("Greece","gre");
		storeTeam("Honduras","hon");
		storeTeam("Iran","irn");
		storeTeam("Italy","ita");
		storeTeam("Japan","jpn");
		storeTeam("Korea Republic","kor");
		storeTeam("Mexico","mex");
		storeTeam("Netherlands","ned");
		storeTeam("Nigeria","nga");
		storeTeam("Portugal","por");
		storeTeam("Paraguay","par");
		storeTeam("Russia","rus");
		storeTeam("Switzerland","sui");
		storeTeam("Slovakia","slv");
		
		storeTeam("Uruguay","uru");
		storeTeam("USA","usa");

		Bet bet= new Bet("test@example.com", "Netherlands", "0", "Spain", "0", "2010-07-11 20:30", "2014-01-12 10:00:00");
		storeBet(bet);
		return null;	
	}

	private String storeTeam(String name, String flag) {
		Team t = new Team(name, flag);
		Key key = ds.store(t);
		return (key != null ? name : null);
	}

	protected String storeGame(String aTeam, String aScore, String bTeam, String bScore, String startTime, String stage, String city, String stadium){
		Game b = new Game(aTeam, aScore, bTeam, bScore, startTime, stage, city, stadium);
		Key key = ds.store( b );
		String calculatedKey=b.getaTeam()+b.getbTeam()+b.getStartTime();
		return (key != null ? calculatedKey : null);
	}

	private String formatGameKey(String aTeam, String aScore, String bTeam, String bScore, String startTime ){
		String sep="|";
		return aTeam+sep+bTeam+sep+startTime;
	}
	public String getBetKey(Bet bet){
		String sep="|";
		String key=bet.getChallenger()
				+sep+bet.getCreatedTime()
				+sep+bet.getaTeam()
				+sep+bet.getaScore()
				+sep+bet.getbTeam()
				+sep+bet.getbScore()
				+sep+bet.getStartTime();
		return key;		
	}
	
	public Game queryGame(String aTeam, String aScore, String bTeam, String bScore, String startTime){
		String gameKey=formatGameKey(aTeam,aScore,bTeam,bScore,startTime);
		return ds.load(Game.class, gameKey);
	}

	public List<Game> listGames(Game game) {
		List<Game> gameList = null;
		if(game!=null){
			if("Past Games".equals(game.getOption())){
				gameList=listPastGames(game);
			}else if("Future Games".equals(game.getOption())){
				gameList=listFutureGames(game);
			}else{
				gameList = listAllGames(game);
			}
		}else{
			gameList = listAllGames(game);
		}
		return gameList;
	}
	
	public List<Game> listAllGames(Game game) {
		List<Game> gameList =new ArrayList<Game>();
		QueryResultIterator<Game> result = 
				ds.find().type(Game.class)
				.addSort("startTime", SortDirection.ASCENDING)
				.now();
		while( result.hasNext()){
			Game g=result.next();
			gameList.add(g);
		}
		return gameList;
	}
	
	public List<Game> listFutureGames(Game game) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		String startTime=sdf.format( new Date() );	
		List<Game> gameList =new ArrayList<Game>();
		QueryResultIterator<Game> result = 
				ds.find().type(Game.class)
				.addFilter("startTime", Query.FilterOperator.GREATER_THAN, startTime)
				.addSort("startTime", SortDirection.ASCENDING)
				.now();
		while( result.hasNext()){
			Game g=result.next();
			gameList.add(g);
		}
		return gameList;
	}
	
	public List<Game> listPastGames(Game game) {
		String currentTime=DateFormatter.getCurrentTime();
		List<Game> gameList =new ArrayList<Game>();
		QueryResultIterator<Game> result = 
				ds.find()
				.type(Game.class)
				.addFilter("startTime", Query.FilterOperator.LESS_THAN, currentTime)
				.addSort("startTime", SortDirection.ASCENDING)
				.now();
		while( result.hasNext()){
			Game g=result.next();
			gameList.add(g);
		}
		return gameList;
	}
	
	public Challenger loadChallenger( String email)
	{
		return ds.load( Challenger.class, email );
	}
	
	public String storeChallenger(Challenger chall)
	{
		Key key = ds.store( chall );
		return (key != null ? chall.getEmail() : null);
	}
	
	public String updateChallenger(Challenger chall){
		ds.update( chall );
		return (chall != null ? chall.getEmail() : null);
	}
	
	public Bet loadBet(Bet bet){
		return ds.load(Bet.class, bet.getId());
	}
	
	public String storeBet(Bet bet){
		String currentTime=DateFormatter.getCurrentTime();
		bet.setCreatedTime(currentTime );
		bet.setCreated(new Date());
		bet.setStatus("open");
		bet.setSuccess(false);
		
		bet.setId(  getBetKey(bet) );
		
		String strkey = bet.getId();
		Key key =null;	
		Bet existingBet = ds.load( Bet.class, strkey );
		
		if(existingBet != null ){
			strkey =existingBet.getId();
		}else{
			key = ds.store( bet );	
		}
		return (key != null ? bet.getId() : null);	
	}
	
	public String updateBet(Bet bet){
		ds.update( bet );  
		return  bet.getId() ;	
	}

	public void increasePoints(String email) {
		Challenger chall = ds.load( Challenger.class, email );
		if(chall!=null){
			chall.setTries(  chall.getTries()+1);
			chall.setHits(  chall.getHits()+1);
			chall.setAverageHits( chall.getHits() /chall.getTries()  ) ;
			chall.setPoints(  chall.getPoints()+1);
			updateChallenger(chall);
		}
	}

	public void decreasePoints(String email) {
		Challenger chall = ds.load( Challenger.class, email );
		if(chall!=null){
			chall.setTries(  chall.getTries()+1 );
			chall.setAverageHits( chall.getHits() /chall.getTries()  ) ;
			chall.setPoints(  chall.getPoints()-1);
			updateChallenger(chall);
		}
	}
	
	public List<Bet> listBets(String email) {
		List<Bet> betList = null;
		if(email!=null){
			betList = new ArrayList<Bet>();
			QueryResultIterator<Bet> result = 
					ds.find().type(Bet.class)
					.addFilter("challenger", Query.FilterOperator.EQUAL, email)
					.addSort("createdTime", SortDirection.DESCENDING)
					.now();
			while( result.hasNext()){
				Bet b=result.next();
				betList.add(b);
			}
		}
		return betList;
	}

	public void closeAllBets() {	
		QueryResultIterator<Bet> result = 
				ds.find().type(Bet.class)
				.addFilter("status", Query.FilterOperator.EQUAL, "open")
				.now();
		while( result.hasNext()){
			Bet b=result.next();
			closeBet(b);
		}
	}

	public String closeBet(Bet b){
		String output="";
		if(b != null){
			if(b.getChallenger()!=null){
				Challenger foundchall = loadChallenger(b.getChallenger());
				Bet foundbet = loadBet(b);
				Game realGame = queryGame(b.getaTeam(), b.getaScore(), b.getbTeam(), b.getbScore(), b.getStartTime());
				if(foundchall!=null && foundbet !=null && realGame !=null ){	
					if( GameTools.hasEnded( realGame )   ){
						if(ScoreValidator.check( realGame, foundbet)){
							output="Your score is Correct! Congrats!";						
							foundbet.setStatus("closed");
							foundbet.setSuccess(true);
							updateBet(foundbet);
							increasePoints( b.getChallenger() );
						}else{
							output="Sorry that was not the score. Keep on trying.";
							foundbet.setStatus("closed");
							foundbet.setSuccess(false);
							updateBet(foundbet);
							decreasePoints( b.getChallenger() );
						}
					}
				}
			}
		}
		return output;
	}

	public List<Team> listTeams(Team team) {
		setupDB();
		List<Team> teamList =  new ArrayList<Team>();
		QueryResultIterator<Team> result = ds.find().type(Team.class).now();
		while( result.hasNext()){
			Team b=result.next();
			teamList.add(b);
		}
		return teamList;
	}

	public void storeTrainingBet(Bet bet, boolean isAnswerOk) {
		if(bet!=null && bet.getChallenger()!=null){
			String email=bet.getChallenger();
			Challenger chall = ds.load( Challenger.class, email );
			if(chall!=null){
				if(isAnswerOk){
					chall.setTrainingHits(   chall.getTrainingHits()+1);
					chall.setTrainingPoints(   chall.getTrainingPoints()+1);
				}else{
					chall.setTrainingPoints(   chall.getTrainingPoints()-1);
				}
				chall.setTrainingTries(  chall.getTrainingTries()+1);
				updateChallenger(chall);
			}
		}
	}
}
